#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <cstring>
#include <malloc.h>
#include <stdio.h>
/*
 * include <locale.h> or the non?standard X substitutes
 * depending on the X_LOCALE compilation flag
 */
#include <X11/Xatom.h>
#include <X11/Xlocale.h>
#include <X11/extensions/shape.h>
#include <cstdlib>
//#include <stdlib>
#include <X11/extensions/Xfixes.h>
#include <X11/extensions/shape.h>
#include <array>
#include <iostream>
#include <stdarg.h>

typedef struct
{
    unsigned long flags;
    unsigned long functions;
    unsigned long decorations;
    long inputMode;
    unsigned long status;
} PropMotifWmHints;

/* bit definitions for MwmHints.flags */
#define MWM_HINTS_FUNCTIONS (1L << 0)
#define MWM_HINTS_DECORATIONS (1L << 1)
#define MWM_HINTS_INPUT_MODE (1L << 2)
#define MWM_HINTS_STATUS (1L << 3)

/* bit definitions for MwmHints.functions */
#define MWM_FUNC_ALL (1L << 0)
#define MWM_FUNC_RESIZE (1L << 1)
#define MWM_FUNC_MOVE (1L << 2)
#define MWM_FUNC_MINIMIZE (1L << 3)
#define MWM_FUNC_MAXIMIZE (1L << 4)
#define MWM_FUNC_CLOSE (1L << 5)

/* bit definitions for MwmHints.decorations */
#define MWM_DECOR_ALL (1L << 0)
#define MWM_DECOR_BORDER (1L << 1)
#define MWM_DECOR_RESIZEH (1L << 2)
#define MWM_DECOR_TITLE (1L << 3)
#define MWM_DECOR_MENU (1L << 4)
#define MWM_DECOR_MINIMIZE (1L << 5)
#define MWM_DECOR_MAXIMIZE (1L << 6)

#define PROP_MOTIF_WM_HINTS_ELEMENTS 5

static Atom _MOTIF_WM_HINTS;
static Atom WM_STATE;

void xf_SetWindowDecorations(Display *dpy, Window window, bool show)
{
    _MOTIF_WM_HINTS = XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
    PropMotifWmHints hints;
    hints.decorations = (show) ? MWM_DECOR_ALL : 0;
    hints.functions = MWM_FUNC_ALL;
    hints.flags = MWM_HINTS_DECORATIONS | MWM_HINTS_FUNCTIONS;
    hints.inputMode = 0;
    hints.status = 0;
    XChangeProperty(dpy, window, _MOTIF_WM_HINTS, _MOTIF_WM_HINTS, 32, PropModeReplace, (unsigned char *)&hints, PROP_MOTIF_WM_HINTS_ELEMENTS);
}

static const char *x11_event_string(int event)
{
    switch (event) {
    case KeyPress:
        return "KeyPress";

    case KeyRelease:
        return "KeyRelease";

    case ButtonPress:
        return "ButtonPress";

    case ButtonRelease:
        return "ButtonRelease";

    case MotionNotify:
        return "MotionNotify";

    case EnterNotify:
        return "EnterNotify";

    case LeaveNotify:
        return "LeaveNotify";

    case FocusIn:
        return "FocusIn";

    case FocusOut:
        return "FocusOut";

    case KeymapNotify:
        return "KeymapNotify";

    case Expose:
        return "Expose";

    case GraphicsExpose:
        return "GraphicsExpose";

    case NoExpose:
        return "NoExpose";

    case VisibilityNotify:
        return "VisibilityNotify";

    case CreateNotify:
        return "CreateNotify";

    case DestroyNotify:
        return "DestroyNotify";

    case UnmapNotify:
        return "UnmapNotify";

    case MapNotify:
        return "MapNotify";

    case MapRequest:
        return "MapRequest";

    case ReparentNotify:
        return "ReparentNotify";

    case ConfigureNotify:
        return "ConfigureNotify";

    case ConfigureRequest:
        return "ConfigureRequest";

    case GravityNotify:
        return "GravityNotify";

    case ResizeRequest:
        return "ResizeRequest";

    case CirculateNotify:
        return "CirculateNotify";

    case CirculateRequest:
        return "CirculateRequest";

    case PropertyNotify:
        return "PropertyNotify";

    case SelectionClear:
        return "SelectionClear";

    case SelectionRequest:
        return "SelectionRequest";

    case SelectionNotify:
        return "SelectionNotify";

    case ColormapNotify:
        return "ColormapNotify";

    case ClientMessage:
        return "ClientMessage";

    case MappingNotify:
        return "MappingNotify";

    case GenericEvent:
        return "GenericEvent";

    default:
        return "UNKNOWN";
    };
}

/**
 * Post an event from the client to the X server
 */
void xf_SendClientEvent(Display *display, Window window, Atom atom, unsigned int numArgs, ...)
{
    XEvent xevent = {0};
    unsigned int i;
    va_list argp;
    va_start(argp, numArgs);

    xevent.xclient.type = ClientMessage;
    xevent.xclient.serial = 0;
    xevent.xclient.send_event = False;
    xevent.xclient.display = display;
    xevent.xclient.window = window;
    xevent.xclient.message_type = atom;
    xevent.xclient.format = 32;

    for (i = 0; i < numArgs; i++) {
        xevent.xclient.data.l[i] = va_arg(argp, int);
    }

    printf("Send ClientMessage Event: wnd=0x%04lX", (unsigned long)xevent.xclient.window);
    XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xevent);
    XSync(display, False);
    va_end(argp);
}

/* 编译方法： g++ xlib_movewindow.cpp -o xlib_movewindow -lXext -lX11 -lXfixes  */
int main(int argc, char **argv)
{
    Display *dpy;
    XEvent event;
    Window win;
    int screen;
    Pixmap mask;
    GC gc;
    XGCValues gcv;
    int h = 500, w = 500, bwidth = 0;

    if ((dpy = XOpenDisplay(NULL)) == NULL) {
        exit(1);
    }

    WM_STATE = XInternAtom(dpy, "WM_STATE", False);

    screen = DefaultScreen(dpy);

    win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0, 500, 200, bwidth, BlackPixel(dpy, screen), WhitePixel(dpy, screen));
    printf("window id=0x%lx", win);

    mask = XCreatePixmap(dpy, win, w, h, 1);
    if (mask == None) {
        return 0;
    }
    gc = XCreateGC(dpy, mask, 0, &gcv);
    XSetForeground(dpy, gc, 0);
    XFillRectangle(dpy, mask, gc, 0, 0, w, h);
    XSetForeground(dpy, gc, 1);

    int input_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask
        | Button1MotionMask | Button2MotionMask | Button3MotionMask | Button4MotionMask | Button5MotionMask | ButtonMotionMask | KeymapStateMask
        | ExposureMask | VisibilityChangeMask | StructureNotifyMask | SubstructureNotifyMask | SubstructureRedirectMask | FocusChangeMask
        | PropertyChangeMask | ColormapChangeMask | OwnerGrabButtonMask;

    XSelectInput(dpy, win, input_mask);

    // xf_SetWindowDecorations(dpy, win, 1);
    // XSetWindowAttributes xswa;
    // xswa.override_redirect = True;
    // XChangeWindowAttributes(dpy, win, CWOverrideRedirect, &xswa);
    XMapWindow(dpy, win);

    // XMoveWindow(dpy, win, -250, 100);
    while (1) {
        XNextEvent(dpy, &event);
        printf("%s Event(%d): wnd=0x%08lX\n", x11_event_string(event.type), event.type, (unsigned long)event.xany.window);
        switch (event.type) {
        case KeyPress: {
            XIconifyWindow(dpy, win, DefaultScreen(dpy));
            break;
        }
        default:
            break;
        }
    }
}
